﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.ESGRatings.BusinessLogic.ESGProfile;
using Sustainalytics.ESGRatings.BusinessLogic.Exceptions;
using Sustainalytics.Utils;
using Sustainalytics.Utils.WebApi.ErrorCodes;
using System.Web.Http.Cors;

namespace Sustainalytics.ESGRatings.Service.Controllers
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    //  [ClaimsChecker("api/v1/company/esg")]
    [RoutePrefix("api/v1/esgprofile")]
    [LogControllerFilter(true, true, false)]
    public class ESGProfileController : ApiController
    {
        private readonly IESGProfileManagement _businessLogicProfile;
        private readonly CommonEventSource _log;

        public ESGProfileController()
        {
            _log = CommonEventSource.Log;

            _businessLogicProfile = new ESGProfileManagement(new RepositoryCollection<ESGProfile>(ClientPlatform.GetConnectionString()),
                                                             new RepositoryCollection<ReferenceUniverse>(ClientDWConnection.GetConnectionString()),
                                                             new RepositoryCollection<WeightMatrix>(ClientDWConnection.GetConnectionString()));
        }

        public ESGProfileController(IESGProfileManagement profileManager)
        {
            _businessLogicProfile = profileManager;

        }



        [Route("")]
        [HttpGet]
        public HttpResponseMessage Get(Guid? accountId, ListOptions options)
        {
            try
            {
                if (accountId == null)
                {
                    return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed });
                }
                List<ESGProfile> esgProfileData = _businessLogicProfile.LoadESGProfiles(accountId, options);
                if (esgProfileData.Any())
                {
                    var profileDtos = esgProfileData.Select(ESGProfileDto.FromESGProfile).ToList();
                    return Request.CreateResponse(HttpStatusCode.OK, profileDtos);
                }
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            catch (ArgumentNullException ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull });
            }
            catch (Exception ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

        }

        [Route("")]
        [HttpPut]
        public HttpResponseMessage Put([FromBody] ESGProfileDto profileDto)
        {
            try
            {
                if (profileDto == null || profileDto.Id == null)
                    throw new EsgProfileObjectNullException();

                Guid? accountId = profileDto.AccountId;
                Guid esgProfileId = profileDto.Id;

                bool nameAlreadyExists = _businessLogicProfile.FindEsgProfileNameDuplicate(profileDto.Name, esgProfileId);
                if (nameAlreadyExists)
                    throw new EsgProfileInvalidException();

                ESGProfile currentProfile = _businessLogicProfile.LoadESGProfileForAccount(accountId, esgProfileId);

                //The Default Profile cannot be edited, ensure that..                
                if (profileDto.AccountId == null)
                {
                    return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed });
                }

                if (accountId != null && esgProfileId != null && !string.IsNullOrEmpty(profileDto.Name) &&
                    !string.IsNullOrEmpty(profileDto.MatrixName)
                    && !string.IsNullOrEmpty(profileDto.ReferenceUniverseName)
                    && profileDto.Active != null && profileDto.MatrixId != null
                    && profileDto.ReferenceUniverseId != null)
                {
                    ESGProfile esgProfileToUpdate = ESGProfileDto.ToESGProfile(profileDto);

                    ESGProfile activeProfile = _businessLogicProfile.LoadESGProfileActiveState(esgProfileToUpdate.AccountId);
                    ESGProfile suggestedProfile = _businessLogicProfile.LoadESGProfileSuggestedState(esgProfileToUpdate.AccountId);

                    //If state is intended to be "Active"
                    if (esgProfileToUpdate.Active == ActiveTypeEnum.Active)
                    {
                        //If there is already another active profile
                        if (activeProfile != null && activeProfile.Id != esgProfileToUpdate.Id)
                        {
                            if (suggestedProfile != null)
                            {
                                if (suggestedProfile.Id != esgProfileToUpdate.Id)
                                {
                                    esgProfileToUpdate.Active = ActiveTypeEnum.NotActive;
                                }
                                else
                                {
                                    esgProfileToUpdate.Active = ActiveTypeEnum.Suggested;
                                }

                            }
                            else
                            {
                                esgProfileToUpdate.Active = ActiveTypeEnum.Suggested;
                            }
                        }
                    }
                    if (esgProfileToUpdate.Active == ActiveTypeEnum.Suggested)
                    {
                        //If there is already a Suggested Profile, ant it's not this, ActiveState will be NotActive
                        if (suggestedProfile != null && suggestedProfile.Id != esgProfileToUpdate.Id)
                        {
                            esgProfileToUpdate.Active = ActiveTypeEnum.NotActive;
                        }
                    }

                    _businessLogicProfile.UpdateESGProfile(accountId, esgProfileId, esgProfileToUpdate);

                    return Request.CreateResponse(HttpStatusCode.OK);
                }

                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputContentDataMissing });
            }
            catch (EsgProfileInvalidException)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "ESG Profile name already exists. Please choose another name for the ESG Profile.");
            }
            catch (EsgProfilesMultipleActiveException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                   new
                   {
                       errCode = "EsgProfilesMultipleActiveException",
                       message = "More than one ESG Profile is Active for this account. Please make sure that only one ESG Profile is activated."
                   });
            }
            catch (EsgProfileObjectNullException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new
                    {
                        errCode = "EsgProfileObjectNullException",
                        message = "ESG Profile object is null. Please make sure that a valid ESG Profile is selected."
                    });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = 1010 });
            }
        }

        [Route("")]
        [HttpPost]
        public HttpResponseMessage Post([FromBody]  ESGProfileDto esgDto)
        {
            try
            {
                if (esgDto == null)
                    throw new EsgProfileAddNullException();

                Guid? accountId = esgDto.AccountId;
                if (accountId != null && esgDto.Id != null && !string.IsNullOrEmpty(esgDto.Name) &&
                    !string.IsNullOrEmpty(esgDto.MatrixName)
                    && !string.IsNullOrEmpty(esgDto.ReferenceUniverseName)
                    && esgDto.Active != null && esgDto.MatrixId != null
                    && esgDto.ReferenceUniverseId != null)
                {
                    //Find ESG Profile Name Uniqueness - if not uniue =>throw Exception
                    bool nameAlreadyExists = _businessLogicProfile.FindEsgProfileNameDuplicate(esgDto.Name, esgDto.Id);
                    if (nameAlreadyExists)
                        throw new EsgProfileInvalidException();

                    ESGProfile esgProfileToCreate = ESGProfileDto.ToESGProfile(esgDto);

                    //Assign NEW GUID, since the one from UI is hardcoded
                    esgProfileToCreate.Id = Guid.NewGuid();

                    SetValidState(esgProfileToCreate);

                    _businessLogicProfile.CreateESGProfile(esgProfileToCreate);

                    return Request.CreateResponse(HttpStatusCode.OK);
                }

                return Request.CreateResponse(HttpStatusCode.BadRequest,
                     new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputContentDataMissing });
            }
            catch (EsgProfilesTooManyException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new
                    {
                        ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed,
                    });
            }
            catch (EsgProfileInvalidException)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "ESG Profile name already exists. Please choose another name for the ESG Profile.");
            }
            catch (EsgProfileAddNullException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new
                    {
                        ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed,
                        message = "ESG Profile had no content. Please make sure that a valid ESG Profile is created."
                    });
            }
            catch (ArgumentNullException ex)
            {
                _log.Exception(ex);
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = 1010 });
            }
        }

        private void SetValidState(ESGProfile esgProfileToUpdate)
        {
            //If esfProfile desired  ActiveState = Active, and there is another one Active per account => ActiveState will be "Suggested"
            ESGProfile activeProfile = _businessLogicProfile.LoadESGProfileActiveState(esgProfileToUpdate.AccountId);
            ESGProfile suggestedProfile = _businessLogicProfile.LoadESGProfileSuggestedState(esgProfileToUpdate.AccountId);

            if (esgProfileToUpdate.Active == ActiveTypeEnum.Active)
            {
                if (activeProfile != null)
                {
                    if (suggestedProfile != null)
                    {
                        esgProfileToUpdate.Active = ActiveTypeEnum.NotActive;
                    }
                    else
                    {
                        esgProfileToUpdate.Active = ActiveTypeEnum.Suggested;
                    }
                }
            }
            if (esgProfileToUpdate.Active == ActiveTypeEnum.Suggested)
            {

                if (suggestedProfile != null)
                {
                    esgProfileToUpdate.Active = ActiveTypeEnum.NotActive;
                }
            }
        }

        [Route("{id}")]
        [HttpDelete]
        public HttpResponseMessage Delete(Guid? accountId, Guid esgProfileId)
        {
            try
            {
                if (esgProfileId.Equals(Guid.Empty))
                    throw new EsgProfileAddNullException();

                ESGProfile currentProfile = _businessLogicProfile.LoadESGProfileForAccount(accountId, esgProfileId);

                // Delete the Default Profile, or an Active profile is not allowed.                 
                if (accountId == null || currentProfile.Active == ActiveTypeEnum.Active)
                {
                    throw new EsgProfileIllegalToDeleteException();
                }

                _businessLogicProfile.DeleteESGProfileForAccount(accountId, esgProfileId);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (EsgProfileUnidentifiedException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new
                    {
                        errCode = "EsgProfileUnidentifiedException",
                        message = "ESG Profile to be deleted does not exist. Please make sure that a valid ESG Profile is selected for deletion."
                    });
            }
            catch (EsgProfileIllegalToDeleteException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed });
            }
            catch (EsgProfileAddNullException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new
                    {
                        errCode = "EsgProfileAddNullException",
                        message = "The operation to delete the designated ESG Profile is not allowed. Please make sure that a valid ESG Profile is selected for deletion."
                    });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = 1010 });
            }
        }




        /// <summary>
        /// Deletes all ESG Profiles related to an Account Id
        /// </summary>
        /// <param name="esgProfileId">The Account Id for which to delete the Esg Profiles</param>
        /// <returns>A HttpResponseMessage with the result</returns>
        [Route("profile/{esgProfileId}")]
        [HttpDelete]
        public HttpResponseMessage Delete(Guid esgProfileId)
        {
            try
            {
                if (esgProfileId == null)
                    throw new EsgProfileIllegalToDeleteException();

                ESGProfile esgProfile = _businessLogicProfile.LoadESGProfile(esgProfileId);

                // Delete the Default Profile  is not an allowed operation
                if (esgProfile == null || esgProfile.AccountId == null)
                {
                    throw new EsgProfileIllegalToDeleteException();
                }

                _businessLogicProfile.DeleteESGProfile(esgProfileId);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (EsgProfileUnidentifiedException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                 new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
            }
            catch (EsgProfileIllegalToDeleteException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed });
            }
            catch (EsgProfileObjectNullException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                  new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = 1010 });
            }
        }

        //?? Route correct?
        /// <summary>
        /// Deletes all Inactive ESG Profiles associated to an Account Id that are in Inactive State
        /// </summary>
        /// <param name="accountId">The Account Id for which to delete the Esg Profiles</param>
        /// <param name="activeState">The Active State associated to the Esg Profile, with the remark that profiles in "Active" and "Suggested" States will not be deleted</param>
        /// <returns>A HttpResponseMessage with the result</returns>
        [Route("account/{accountId}")]
        [HttpDelete]
        public HttpResponseMessage Delete(Guid? accountId, ActiveTypeEnum activeState)
        {
            try
            {
                // Delete the Default Profile, or an Active profile is not allowed.
                if (accountId == null)
                    throw new EsgProfileIllegalToDeleteException();

                _businessLogicProfile.DeleteAllIactiveESGProfilesForAccount(accountId, activeState);
                return Request.CreateResponse(HttpStatusCode.OK);
            }
            catch (EsgProfileUnidentifiedException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                 new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
            }
            catch (EsgProfileIllegalToDeleteException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.OperationNotAllowed });
            }
            catch (EsgProfileObjectNullException)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest,
                  new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = 1010 });
            }
        }



        /// <summary>
        /// Gets all ESG Profiles associated to an Account Id or the ESG Default Profile for which the Account Id is null
        /// </summary>
        /// <param name="accountId">The Account Id to retrieve the ESG Profiles for or null for the Default Profile</param>
        /// <returns>The Response with the ESG Profiles List</returns>
        [Route("account/{accountId}/profiles")]
        [HttpGet]
        public HttpResponseMessage Get(Guid? accountId)
        {
            try
            {
                //Get Request for the ESG Default Profile for which the accountId is null
                if (accountId == null)
                {
                    ESGProfile defaultEsgProfileDto = _businessLogicProfile.LoadDefaultEsgProfile();
                    if (defaultEsgProfileDto == null)
                    {
                        return Request.CreateResponse(HttpStatusCode.NoContent, new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
                    }
                    return Request.CreateResponse(HttpStatusCode.OK, defaultEsgProfileDto);
                }

                List<ESGProfile> esgProfileData = _businessLogicProfile.LoadESGProfiles(accountId);
                if (esgProfileData.Any())
                {
                    List<ESGProfileDto> profileDtos =
                        esgProfileData.Select(esgProfileItem => ESGProfileDto.FromESGProfile(esgProfileItem)).ToList();

                    return Request.CreateResponse(HttpStatusCode.OK, profileDtos);
                }
                else
                {
                    return Request.CreateResponse(HttpStatusCode.NoContent, new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound });
                }

            }
            catch (ArgumentNullException ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull });
            }
            catch (Exception ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

        /// <summary>
        /// Gets the Active ESG Profile associated to an Account Id
        /// </summary>
        /// <param name="accountId">The Account Id to retrieve the ESG Profiles for</param>
        /// <returns>The ESG Profiles List</returns>
        [Route("{accountId}")]
        [HttpGet]
        public HttpResponseMessage Get(Guid accountId, ActiveTypeEnum activeState)
        {
            try
            {
                ESGProfile esgProfileData = _businessLogicProfile.LoadESGProfileActiveState(accountId);
                if (esgProfileData != null)
                {
                    ESGProfileDto profileDto = ESGProfileDto.FromESGProfile(esgProfileData);

                    return Request.CreateResponse(HttpStatusCode.OK, profileDto);
                }
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            catch (ArgumentNullException ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull });
            }
            catch (Exception ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

        [Route("account/{accountId}/profile/{esgProfileId}")]
        [HttpGet]
        public HttpResponseMessage Get(Guid accountId, Guid esgProfileId)
        {
            try
            {
                ESGProfile esgProfileDataEntity = _businessLogicProfile.LoadESGProfileForAccount(accountId, esgProfileId);
                if (esgProfileDataEntity != null)
                {
                    ESGProfileDto profileDto = ESGProfileDto.FromESGProfile(esgProfileDataEntity);

                    return Request.CreateResponse(HttpStatusCode.OK, profileDto);
                }
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            catch (ArgumentNullException ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.BadRequest,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull });
            }
            catch (Exception ex)
            {
                _log.Exception(ex);
                return this.Request.CreateResponse(HttpStatusCode.InternalServerError,
                    new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
        }

    }


}


